﻿using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System.ComponentModel.DataAnnotations;

namespace _05_Options
{
    public class MvcOptions
    {
        [RegularExpression(@"^aa",ErrorMessage ="必须以aa开头")]
        public string? Url { get; set; }
    }

    internal class Program
    {
        static void Main(string[] args)
        {
            TestChange();
        }
        /// <summary>
        /// 验证执行顺序
        /// </summary>
        static void TestSort()
        {
            var services = new ServiceCollection();
            //name为Empty
            services.Configure<MvcOptions>(a =>
            {
                Console.WriteLine(11);
                a.Url = "132";
            });
            services.PostConfigure<MvcOptions>(a =>
            {
                Console.WriteLine(33);
                a.Url = "132";
            });
            services.Configure<MvcOptions>(a =>
            {
                Console.WriteLine(22);
                a.Url = "132";
            });
            services.PostConfigure<MvcOptions>(a =>
            {
                Console.WriteLine(44);
                a.Url = "132";
            });
            var sp = services.BuildServiceProvider();
            var optionsFactory = sp.GetRequiredService<IOptionsFactory<MvcOptions>>();
            var options = optionsFactory.Create(Options.DefaultName);
        }
        /// <summary>
        /// 解析选项
        /// </summary>
        public void TestResolver()
        {
            var services = new ServiceCollection();
            services.Configure<MvcOptions>(a =>
            {
                a.Url = "132";
            });
            var sp = services.BuildServiceProvider();
            var optionsFactory = sp.GetRequiredService<IOptionsFactory<MvcOptions>>();
            var options = sp.GetRequiredService<IOptions<MvcOptions>>();
            var optionsMonitor = sp.GetRequiredService<IOptionsMonitor<MvcOptions>>();
            var optionsSnapshot = sp.GetRequiredService<IOptionsSnapshot<MvcOptions>>();
        }
        /// <summary>
        /// 直接将选项注册到容器
        /// </summary>
        static void TestDiOptions()
        {
            var services = new ServiceCollection();
            services.Configure<MvcOptions>(a =>
            {
                Console.WriteLine(22);
                a.Url = "132";
            });
            services.AddSingleton(sp => 
            {
                return sp.GetRequiredService<IOptions<MvcOptions>>().Value;
            });
            //必须使用Scoped，因为IOptionsSnapshot是Scoped的,如果是单实列的话，就丢失了自动更改的性质了
            //services.AddScoped(sp =>
            //{
            //    return sp.GetRequiredService<IOptionsSnapshot<MvcOptions>>().Value;
            //});
            var sp = services.BuildServiceProvider();
            var options = sp.GetRequiredService<MvcOptions>();
        }
        /// <summary>
        /// 基本使用
        /// </summary>
        static void TestBasic()
        {
            var services = new ServiceCollection();
            //name为Empty
            services.Configure<MvcOptions>(a =>
            {
                a.Url = "132";
            });
            services.PostConfigure<MvcOptions>(a =>
            {
                a.Url = "132";
            });
            services.AddTransient(sp =>
            {
                return sp.GetRequiredService<IOptions<MvcOptions>>().Value;
            });
            var container = services.BuildServiceProvider();
            //获取IOptions<MvcOptions>的实列
            var options0 = container.GetRequiredService<MvcOptions>();
            //获取IOptions<MvcOptions>的实列，底层是IOptionsFactory<MvcOptions>
            var options1 = container.GetRequiredService<IOptions<MvcOptions>>();
            //获取IOptions<MvcOptions>的实列，可以自动更新
            var options2 = container.GetRequiredService<IOptionsMonitor<MvcOptions>>();
            //获取IOptions<MvcOptions>的实列，可以自动更新，可以注册变更通知
            var options3 = container.GetRequiredService<IOptionsSnapshot<MvcOptions>>();
            //获取IOptionsFactory<MvcOptions>的实列基础
            var options4 = container.GetRequiredService<IOptionsFactory<MvcOptions>>();
        }

        /// <summary>
        /// 更改监听
        /// </summary>
        public static void TestChange()
        {
            var services = new ServiceCollection();
            var configuration = GetConfiguration();
            //IConfiguration接口可以获取IChangeToken，IChangeToken可以注册回调
            services.Configure<MvcOptions>(configuration.GetSection("MvcOptions"));
            var container = services.BuildServiceProvider();
            while (true)
            {
                Thread.Sleep(1000);
                using (var scope = container.CreateScope())
                {
                    var o1 = scope.ServiceProvider.GetRequiredService<IOptions<MvcOptions>>();
                    var o2 = scope.ServiceProvider.GetRequiredService<IOptionsSnapshot<MvcOptions>>();
                    var o3 = scope.ServiceProvider.GetRequiredService<IOptionsMonitor<MvcOptions>>();
                    Console.WriteLine("==================");
                    Console.WriteLine($"IOptions:{o1.Value.Url}");
                    Console.WriteLine($"IOptionsSnapshot:{o2.Value.Url}");
                    Console.WriteLine($"IOptionsMonitor:{o3.CurrentValue.Url}");
                    o3.OnChange(o =>
                    {
                        Console.WriteLine("选项发生更改了");
                    });
                }
            }
        }
        /// <summary>
        /// 命名选项
        /// </summary>
        public static void TestNamed()
        {
            var services = new ServiceCollection();
            var configuration = GetConfiguration();
            //默认命名
            services.Configure<MvcOptions>(configuration.GetSection("MvcOptions"));
            services.Configure<MvcOptions>("o1", configuration.GetSection("MvcOptions"));
            services.Configure<MvcOptions>("o2", configuration.GetSection("WebOptions"));
            var container = services.BuildServiceProvider();
            var o1 = container.GetRequiredService<IOptionsSnapshot<MvcOptions>>();
            var o2 = container.GetRequiredService<IOptionsMonitor<MvcOptions>>();
            Console.WriteLine("IOptionsSnapshot:Named:" + o1.Get("o1").Url);
            //Value指的是默认名称的选项
            Console.WriteLine("IOptionsSnapshot:Value:" + o1.Value.Url);
            Console.WriteLine("IOptionsMonitor:Named:" + o2.Get("o2").Url);
            Console.WriteLine("IOptionsMonitor:Value:" + o2.CurrentValue.Url);
        }
        /// <summary>
        /// 后续更新
        /// </summary>
        public static void TestPostConfigure()
        {
            var services = new ServiceCollection();
            services.PostConfigure<MvcOptions>(c =>
            {
                c.Url = "Post123";
            });
            services.Configure<MvcOptions>(c =>
            {
                c.Url = "123";
            });
            var container = services.BuildServiceProvider();
            var o1 = container.GetRequiredService<IOptionsSnapshot<MvcOptions>>();
            var o2 = container.GetRequiredService<IOptionsMonitor<MvcOptions>>();
        }
        /// <summary>
        /// 实现IPostConfigureOptions接口的方式
        /// </summary>
        public static void TestIPostConfigureOptions()
        {
            var services = new ServiceCollection();
            services.Configure<MvcOptions>(s => s.Url = "111");
            services.AddSingleton<IPostConfigureOptions<MvcOptions>, MvcOptionsPostConfigureOptions>();
            var container = services.BuildServiceProvider();
            var o2 = container.GetRequiredService<IOptions<MvcOptions>>();
        }
        /// <summary>
        /// 使用di来配置
        /// </summary>
        public static void TestDep()
        {
            var services = new ServiceCollection();
            //注册di服务
            services.AddSingleton<MvcOptionsDep>();
            services.AddOptions<MvcOptions>()//注册选项
                .Configure(a=>a.Url="123")//第一个配置
                .PostConfigure<MvcOptionsDep>((options, dep) => //后续配置
                {
                    dep.Configure(options);
                });
            var container = services.BuildServiceProvider();
            var o2 = container.GetRequiredService<IOptions<MvcOptions>>();
        }
        /// <summary>
        /// 通过注解验证
        /// </summary>
        public static void TestValidateDataAnnotations()
        {
            try
            {
                var services = new ServiceCollection();
                //不是把选项注入到容器，只是注入了该选项的委托
                services.Configure<MvcOptions>(a => a.Url = "123");
                //得到OptionsBuilder，进行验证，本质还是执行Configure（可以执行无数多个Configre）
                services.AddOptions<MvcOptions>()
                    .ValidateDataAnnotations();
                var container = services.BuildServiceProvider();
                var options = container.GetRequiredService<IOptions<MvcOptions>>();
                Console.WriteLine(options.Value.Url);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
        
        /// <summary>
        /// 通过委托验证
        /// </summary>
        public static void TestValidateDelegate()
        {
            try
            {
                var services = new ServiceCollection();
                //不是把选项注入到容器，只是注入了该选项的委托
                services.Configure<MvcOptions>(a => a.Url = "123");
                //得到OptionsBuilder，进行验证，本质还是执行Configure（可以执行无数多个Configre）
                services.AddOptions<MvcOptions>()
                    .Validate(options => 
                    {
                        if (options.Url == null)
                        {
                            return false;
                        }
                        if (!options.Url.StartsWith("aa"))
                        {
                            return false;
                        }
                        return true;
                    }, "必须以aa开头");
                var container = services.BuildServiceProvider();
                var options = container.GetRequiredService<IOptions<MvcOptions>>();
                Console.WriteLine(options.Value.Url);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
        /// <summary>
        /// 加载配置
        /// </summary>
        /// <returns></returns>
        public static IConfiguration GetConfiguration()
        {
            var configuration = new ConfigurationManager();
            configuration.SetBasePath(Directory.GetCurrentDirectory());
            configuration.AddJsonFile("config.json", false, true);
            return configuration;
        }
    }
}
